/*******************************************************************************
New Unit Technologies, a division of DBI, LLC.

Created for CS450 at West Virginia University, Fall 2003

Developers:	Matthew Bradley
		Andrew Lowery
		Richard Paletta
		J. Ryan Sigler

File name: queue.c
*******************************************************************************/

#include "pcb.h"
#include "mpx_supt.h"


extern PCB *running;
extern queue *READY_QUEUE;             		 	//initialize queues
extern queue *BLOCKED_QUEUE;
extern queue *SUS_BLOCKED_QUEUE;
extern queue *SUS_READY_QUEUE;


/*******************************************************************************
Procedure name: initialize()
Purpose: Initialize the queue
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: *newqueue
Returns: none
Globals: none
Calls: n/a
Change log: 9/30/03 - Ryan and Drew write initial code
	    10/2/03 - Ryan fixes the initializations so that they read NULL
	    10/5/03 - Drew adds comments
*******************************************************************************/

extern char p_msg[80];
extern int p_length;

void initialize (queue *newqueue)   	  		//initializing the queue
{
	newqueue -> back  = NULL;
	newqueue -> front = NULL;
}

/*******************************************************************************
Procedure name: insert()
Purpose: inserts a pcb to the end of a queue
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: *new_pcb, *current_queue
Returns: 0 if pcb does not exist
Globals: none
Calls: n/a
Change log: 9/30/03 - Ryan and Drew write initial code
	    10/1/03 - Ryan fixes warnings
	    10/2/03 - Ryan dubugs insert function
	    10/5/03 - Drew adds comments
	    10/6/03 - Ryan fixes return error
*******************************************************************************/

void insert(PCB *new_pcb, queue *current_queue)
{
	
	if (current_queue -> front == NULL) 			//add element if queue is empty
	{
	  current_queue -> front = new_pcb;
	  current_queue -> back  = new_pcb;
	  current_queue -> count++;
	}

	else     						//add element to end of queue
	{
	  current_queue -> back -> next = new_pcb;
	  new_pcb -> prev = current_queue -> back;
	  current_queue -> count++;
	  current_queue -> back = new_pcb;
	}

	if(current_queue == READY_QUEUE)
		new_pcb->state = READY;

	if(current_queue == BLOCKED_QUEUE)
		new_pcb->state = BLOCKED;

	if(current_queue == SUS_READY_QUEUE)
		new_pcb->state = SUS_READY;

	if(current_queue == SUS_BLOCKED_QUEUE)
		new_pcb->state = SUS_BLOCKED;
}

/*******************************************************************************
Procedure name: pinsert
Purpose: inserts a pcb into a queue based on it's priority
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: *new_pcb, *current_queue
Returns: n/a
Globals: none
Calls: n/a
Change log: 9/30/03 - Ryan and Drew write initial code
	    10/1/03 - Ryan fixes warnings
            10/2/03 - Ryan debugs priority sort
	    10/5/03 - Drew adds comments
	    10/6/03 - Ryan fixes return error
	    10-31-08 - John fixes count error
*******************************************************************************/

void pinsert(PCB *new_pcb, queue *current_queue)
{
	PCB *cur;
	cur = current_queue -> front;
	new_pcb->state = READY;

	if (new_pcb == NULL) 							//checks to make sure PCB exists
	{
		sprintf(p_msg, "\nERROR 20: PCB does not exist\n\n\0");
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);

		
	}

	if (current_queue -> front == NULL) 					//add element if queue is empty
	{
		current_queue -> front = new_pcb;
		current_queue -> back  = new_pcb;
		current_queue -> count++;
	}

	else
	{
		while ((cur != NULL) && (cur -> priority >= new_pcb ->priority))
		{
			cur = cur -> next;
		}

		if (cur == current_queue -> front)			  	//add element to the front of queue
		{
			new_pcb -> next = cur;
			cur -> prev = new_pcb;
			current_queue -> front = new_pcb;
			current_queue -> count++;
		}

		else if (cur == NULL)                       				//add element to the end of queue
		{
			current_queue -> back -> next = new_pcb;
			new_pcb -> prev = current_queue -> back;
			current_queue -> count++;
			current_queue -> back = new_pcb;
		}

		else       							//add element in the middle
		{
			cur -> prev -> next = new_pcb;
			new_pcb -> next = cur;
			new_pcb -> prev = cur -> prev;
			cur -> prev = new_pcb;
			current_queue->count++;
		}
	}
}

/*******************************************************************************
Procedure name: removepcb
Purpose: removes a pcb from a queue
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: *old_pcb, *current_queue
Returns: n/a
Globals: none
Calls: n/a
Change log: 9/30/03 - Ryan and Drew write initial code
	    10/1/03 - Ryan fixes warnings
            10/2/03 - Ryan debugs removepcb
	    10/5/03 - Drew adds comments
	    10/6/03 - Ryan edits comments
	    10/6/03 - Ryan fixes return error
	    10-31-08 - Johnathon fixes count error
*******************************************************************************/

void removepcb (PCB *old_pcb, queue *current_queue)
{
	
	PCB *cur;
	cur = old_pcb;
	
	//printf("%d\n", current_queue->count);

	if (old_pcb == NULL)
	{
		sprintf(p_msg, "\nERROR 20: PCB does not exist\n\n\0");
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);

		
	}

	else
	{
		if (cur == current_queue -> front)
		{



		  if(current_queue->count ==1) {
		    current_queue->front = NULL;
		    current_queue->back = NULL;
		  }
		  else {
		  cur-> next -> prev = NULL;
		  current_queue -> front = cur -> next;
		  
		 }
        //current_queue -> count--;
		cur->next = NULL;
		cur->prev = NULL;
		}/* at front of queue */

		else
		{
			if (cur == current_queue -> back)			//remove from end
			{
				cur -> prev -> next = NULL;
				current_queue -> back = cur -> prev;
				
				//current_queue -> count--;
				cur->next = NULL;
				cur->prev = NULL;
			}

			else   														 	//remove from middle
			{
				cur -> prev -> next = cur -> next;
				cur -> next -> prev = cur -> prev;
				
				//current_queue -> count--;
				cur->next = NULL;
				cur->prev = NULL;
			}
		}/*else not front*/
		}/*else the pcb exists*/
		current_queue->count--;
}

/*******************************************************************************
Procedure name: traversequeue()
Purpose: traverse the queue element by element
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: *current_queue
Returns: n/a
Globals: none
Calls: n/a
Change log: 9/30/03 - Ryan and Drew write initial code
	    10/1/03 - Ryan fixes warning messages
            10/2/03 - Ryan debugs traverse queue function
	    10/5/03 - Drew adds error checking
	    10/5/03 - Drew adds comments
	    10/6/03 - Ryan fixes return error
            10/7/03 - Rich beautifies display
*******************************************************************************/

void traversequeue(queue *current_queue)
{
	PCB *cur;
	char dummy[80];
	int count=600;

	cur = current_queue -> front;

	if (cur == NULL)
	{
		sprintf(p_msg, "\nERROR 21: The queue is empty\n\n\0");
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);
	}

	while (cur != NULL)
	{
		sprintf(p_msg, "***********************************\n\0");
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);

		sprintf(p_msg, "Name:    \t\t%s\n\0",cur->name);
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);

		if(cur->pclass==0)
            {
			sprintf(p_msg, "Class:   \t\tApplication\n\0");
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
		}
			else
			{
				sprintf(p_msg, "Class:   \t\tSystem\n\0");
				p_length = strlen(p_msg);
				sys_req(WRITE, TERMINAL, p_msg, &p_length);
                  }
			sprintf(p_msg, "Priority:\t\t%i\n\0",cur->priority);        //print out attributes
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);


			switch(cur->state)
			{
			case 0:	 sprintf(p_msg, "State:   \t\tRUNNING\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
                        	 break;
			case 1:  sprintf(p_msg, "State:   \t\tREADY\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
                        	 break;
			case 2:  sprintf(p_msg, "State:   \t\tBLOCKED\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
				 break;
			case 3:  sprintf(p_msg, "State:   \t\tSUS_READY\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
				 break;
			case 4:  sprintf(p_msg, "State:   \t\tSUS_BLOCKED\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
				 break;
			default: sprintf(p_msg, "State:   \t\tERROR!!\n\0");
					p_length = strlen(p_msg);
					sys_req(WRITE, TERMINAL, p_msg, &p_length);
                        }
			sprintf(p_msg, "Stack:   \t\t%i\n\0",cur->stack);
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
			sprintf(p_msg, "Size:    \t\t%i\n\0",cur->mem_size);
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
			sprintf(p_msg, "Load add:\t\t%i\n\0",cur->load);
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
			sprintf(p_msg, "Exe add: \t\t%i\n\0",cur->exe);
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
			sprintf(p_msg, "***********************************\n\0");
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);

		sprintf(p_msg, "\nPress \"ENTER\" key to view next PCB in queue\n\n\0");
		p_length = strlen(p_msg);
		sys_req(WRITE, TERMINAL, p_msg, &p_length);

		sys_req(READ,TERMINAL,dummy,&count);

		cur = cur -> next;
	}
}

/*******************************************************************************
Procedure name: findpcb()
Purpose: find a pcb in a queue
Authors Names: Ryan Sigler, Andrew Lowery
Parameters: pcb_to_find[]
Returns: found
Globals: none
Calls: n/a
Change log: 10/4/03 - Ryan and Rich write initial code
            10/5/03 - Ryan and Rich debug findpcb function
            10/5/03 - Drew adds comments
*******************************************************************************/

PCB *findpcb(char pcb_to_find[])
{
	PCB *cur=NULL;
	PCB *found=NULL;

	//check in ready queue

	queue *current_queue = READY_QUEUE;
	cur = current_queue -> front;

	while (cur != NULL)
	{

	
		if((!strncmp(pcb_to_find, cur -> name, strlen(pcb_to_find)))&&(!strncmp(pcb_to_find, cur -> name, strlen(cur->name))))
		{
			found = cur;
		}

		cur = cur -> next;
	}

	//check in suspended ready queue

	current_queue = SUS_READY_QUEUE;
	cur = current_queue -> front;

	while (cur != NULL)
	{
	
	  if(!strncmp(pcb_to_find, cur -> name, strlen(pcb_to_find)))
	  {
	 
		 found = cur;
	  }

	  cur = cur -> next;
	}

	//check in blocked queue

	current_queue = BLOCKED_QUEUE;
	cur = current_queue -> front;

	while (cur != NULL)
	{
	
		if(!strncmp(pcb_to_find, cur -> name, strlen(pcb_to_find)))
		{
		
			found = cur;
		}

		cur = cur -> next;
	}

	//check in suspended blocked

	current_queue = SUS_BLOCKED_QUEUE;
	cur = current_queue -> front;

	while (cur != NULL)
	{

		if(!strncmp(pcb_to_find, cur -> name, strlen(pcb_to_find)))
		{

			found = cur;
		}

		cur = cur -> next;
	}

	return (found);
}
